/*
 * Copyright (C) 2013 Daniel Treffenstädt <daniel@treffenstaedt.de>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

#include "OVector3D.h"

#include "ODegree.h"
#include "OPolar3D.h"
#include "OMethods.h"
#include "OVector2D.h"

O_START_NAMESPACE

Vector3D::Vector3D()
{
	m_x	= 0;
	m_y	= 0;
	m_z	= 0;
	m_valid	= false;
}

Vector3D::Vector3D(double x, double y, double z)
{
	m_x	= x;
	m_y	= y;
	m_z	= z;
	m_valid	= true;
}

Vector3D::Vector3D(Ogre::Vector3 v)
{
	m_x		= v.x;
	m_y		= v.y;
	m_z		= v.z;
	m_valid	= true;
}

bool Vector3D::valid()
{
	return m_valid;
}

double Vector3D::x()
{
	return m_x;
}

double Vector3D::y()
{
	return m_y;
}

double Vector3D::z()
{
	return m_z;
}

double Vector3D::magnitude()
{
	double x	= Methods::pow(this->x(), 2);
	double y	= Methods::pow(this->y(), 2);
	double z	= Methods::pow(this->z(), 2);

	return Methods::root(x + y + z);
}

Polar3D Vector3D::toPolar()
{
	double ground	= Vector2D(x(), z()).magnitude();

	Degree v		= Methods::acos(ground / magnitude());
	if (y() == 0)
	{
		v		= 90;
	}
	else if (y() < 0)
	{
		v	+= 90;
	}
	else
	{
		v	= double(90) - v.deg();
	}

	Degree h;
	if (x() == 0)
	{
		if (z() > 0)
		{
			h	= 0;
		}
		else if (z() < 0)
		{
			h	= 180;
		}
	}
	else if (z() == 0)
	{
		if (x() > 0)
		{
			h	= 90;
		}
		else
		{
			h	= 270;
		}
	}
	else if (z() > 0)
	{
		if (x() > 0)
		{
			h	=	Methods::acos(z() / ground);
		}
		else
		{
			h	=	double(360) - Methods::acos(z() / ground).deg();
		}
	}
	else
	{
		if (x() > 0)
		{
			h	=	Methods::acos(x() / ground) + 90;
		}
		else
		{
			h	=	Methods::acos(Methods::abs(z()) / ground) + 180;
		}
	}

	return Polar3D(v, h, magnitude());
}

Ogre::Vector3 Vector3D::toOgre()
{
	return Ogre::Vector3(x(), y(), z());
}

Vector3D Vector3D::crossProduct(Vector3D v)
{
	return Vector3D(y()*v.z() - z()*v.y(),
					z()*v.x() - x()*v.z(),
					x()*v.y() - y()*v.x());
}

double Vector3D::dotProduct(Vector3D v)
{
	return x()*v.x() + y()*v.y() + z()*v.z();
}

void Vector3D::rotateH(Degree d)
{
	Polar3D p	= *(toPolar().rotateH(d));
	(*this)	= p.toVector();
}

void Vector3D::rotateV(Degree d)
{
	Polar3D p	= *(toPolar().rotateV(d));
	(*this)	= p.toVector();
}

void Vector3D::operator +=(Vector3D v)
{
	m_x	+= v.x();
	m_y	+= v.y();
	m_z	+= v.z();
}

Vector3D Vector3D::operator +(Vector3D v)
{
	return Vector3D(x() + v.x(), y() + v.y(), z() + v.z());
}

void Vector3D::operator -=(Vector3D v)
{
	m_x	-= v.x();
	m_y	-= v.y();
	m_z	-= v.z();
}

Vector3D Vector3D::operator -(Vector3D v)
{
	return Vector3D(x() - v.x(), y() - v.y(), z() - v.z());
}

bool Vector3D::operator <(Vector3D v)
{
	return magnitude() < v.magnitude();
}

bool Vector3D::operator <=(Vector3D v)
{
	return magnitude() <= v.magnitude();
}

bool Vector3D::operator >(Vector3D v)
{
	return magnitude() > v.magnitude();
}

bool Vector3D::operator >=(Vector3D v)
{
	return magnitude() >= v.magnitude();
}

bool Vector3D::operator ==(Vector3D v)
{
	return magnitude() == v.magnitude();
}

bool Vector3D::operator !=(Vector3D v)
{
	return magnitude() != v.magnitude();
}

void Vector3D::operator =(Vector3D v)
{
	m_x		= v.x();
	m_y		= v.y();
	m_z		= v.z();
	m_valid	= v.valid();
}

void Vector3D::operator =(Ogre::Vector3 v)
{
	m_x		= v.x;
	m_y		= v.y;
	m_z		= v.z;
	m_valid	= true;
}

void Vector3D::operator *=(double a)
{
	m_x		*= a;
	m_y		*= a;
	m_z		*= a;
}

Vector3D Vector3D::operator *(double a)
{
	return Vector3D(x() * a, y() * a, z() * a);
}

void Vector3D::operator /=(double a)
{
	if (a != 0)
	{
		m_x		/= a;
		m_y		/= a;
		m_z		/= a;
	}
}

Vector3D Vector3D::operator /(double a)
{
	if (a != 0)
	{
		return Vector3D(x() / a, y() / a, z() / a);
	}
	return Vector3D(x(), y(), z());
}

Vector3D Vector3D::operator- ()
{
	return Vector3D(-x(), -y(), -z());
}

O_END_NAMESPACE
